<!-- 纯净版示范 -->
<template>
  <!-- 最外层容器监mouse系列事件, 用来做节点拖拽 -->
  <div class="diagram-example-vue" id="diagram-example-vue" @mousedown="startNodesBus($event)" @mousemove="moveNodesBus($event)" @mouseup="endNodesBus($event)">
    <!-- 左侧导航 -->
    <div class="page-left">
      <div class="basic-node" v-for="(item, i) in initNodesBasic" :key="'nodes_basic' + i" @mousedown="dragIt(item)">
        <i :class="`${item.iconClassName || 'el-icon-coin'} icon`"></i>
        <span class="node-text">{{item.name}}</span>
      </div> 
    <!--   <el-button type="danger" @click="tapToLogin">点击登录</el-button>
      <el-button type="success" @click="tapGetTree">点击获取-左侧场景树</el-button>
      <el-button type="primary" @click="getEquipmentList">点击获取-设备列表</el-button> -->
      <!-- 暂时模拟左侧场景树 -->
      <ul class="tree-ul">
        <li v-for="(item) in scenceTree" :key="item.id">
          <div @click="selectScenceTree(item.id)" class="item-text">{{item.label}}</div>
          <div v-if="item.children && item.children.length>0">
            <div class="">{{item.children.length}}</div>
            <div v-for="(childrenItem) in item.children" :key="childrenItem.id" @click="selectScenceTree(childrenItem.id)" class="item-text">{{childrenItem.label}}</div>
          </div>
        </li>
      </ul>
    </div>
    <!-- 右侧表单 -->
    <div class="right-form" v-show="isShowRight">
      <!-- <div class="title">这里是编辑区域, 选中节点进行编辑</div>
        <el-form
          v-if="formDetail.currentEditNodeId"
          ref="form"
          :model="formDetail.form"
          label-width="80px"
        >
          <el-form-item label="节点描述">
            <el-input v-model="formDetail.form.details"></el-input>
          </el-form-item>
          <el-form-item label="生成时间">
            <el-input v-model="formDetail.form.createTime"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit">保存变更</el-button>
          </el-form-item>
        </el-form>
  
        <el-form ref="autoLayout" :model="formDetail.layout" label-width="80px">
          <div class="title">前端自动布局体验</div>
          <el-form-item label="节点数量">
            <el-input type="number" v-model="formDetail.form.nodes"></el-input>
          </el-form-item>
          <el-form-item label="连线数量">
            <el-input type="number" v-model="formDetail.form.sides"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="generate">生成新图</el-button>
            <el-button type="primary" @click="autoLayout">自动布局</el-button>
          </el-form-item>
        </el-form> -->
      <el-tabs v-model="activeName" class="config-tabs" @tab-click="rightTabsClick">
        <el-tab-pane label="基本设置" name="first">
          <div class="tab-cont">
            <div class="section-title">设备配置项</div>
            <div class="section-cont">
              <div class="select-title">设备选择</div>
              <el-select v-model="deviceValue" placeholder="请选择" @change="selectDeviceId">
                <el-option v-for="item in deviceOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
              <!-- <div class="select-title">设备类型</div>
              <el-select v-model="deviceTypeValue" placeholder="请选择">
                <el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
              <div class="select-title">源设备选择</div>
              <el-select v-model="deviceParentValue" @change="selectDeviceParentValue" placeholder="请选择">
                <el-option v-for="item in deviceParentOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select> -->
            </div>
            <div class="section-title">监测点选择</div>
            <div class="section-cont">
              <!--
                  <el-radio v-model="radio" label="1">备选项</el-radio>
                  <el-radio v-model="radio" label="2">备选项</el-radio>
                  <el-radio v-model="radio" label="3">备选项</el-radio> 
              -->
              <el-select v-model="observationPointValue" placeholder="请选择" @change='selectPointId'>
                <el-option v-for="item in observationPointOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
              <!-- <el-select multiple collapse-tags v-model='pointArrr' @change='changePointSelect' @remove-tag='removeTag' placeholder='请选择'>
                <el-option label='全选' value='全选' @click.native='selectAllPoint'></el-option>
                <el-option v-for='(item, index) in pointOptions' :key='index' :label='item.name' :value='item.name'></el-option>
              </el-select> -->
            </div>

            <div class="section-title">这是分割线 ——— {{srcNode.nodeType}} </div>
            <template v-if="srcNode.nodeType==1">
              <div class="section-title">源头模型选择</div>
              <div class="section-cont"> 
                <el-select v-model="srcTemplateValue" placeholder="请选择" @change='selectRelationOption($event,"srcTemplate","srcPointOptions")'>
                  <el-option v-for="(item,index) in srcTemplateOptions" :key="index" :label="item.label" :value="item.value" />
                </el-select> 
              </div>
            </template>
            <template v-if="srcNode.nodeType==0">
              <div class="section-title">源头设备选择</div>
              <div class="section-cont"> 
                <el-select v-model="srcDeviceValue" placeholder="请选择" @change='selectRelationOption($event,"srcDevice","srcPointOptions")'>
                  <el-option v-for="(item,index) in srcDeviceOptions" :key="index" :label="item.label" :value="item.value" />
                </el-select> 
              </div> 
            </template>         

            <div class="section-title">源监测点选择</div>
            <div class="section-cont"> 
              <el-select v-model="srcPointValue" placeholder="请选择" @change='selectRelationOption($event,"srcPoint","srcPointOptions")'>
                <el-option v-for="(item,index) in srcPointOptions" :key="index" :label="item.label" :value="item.value" />
              </el-select> 
            </div>   

            <template v-if="targetNode.nodeType==1">
              <div class="section-title">目标模型</div>
              <div class="section-cont"> 
                <el-select v-model="targetTemplateValue" placeholder="请选择" @change='selectRelationOption($event,"targetTemplate","targetPointOptions")'>
                  <el-option v-for="(item,index) in targetTemplateOptions" :key="index" :label="item.label" :value="item.value" />
                </el-select> 
              </div> 
            </template>

            <template v-if="targetNode.nodeType==0">
              <div class="section-title">目标设备选择</div>
              <div class="section-cont"> 
                <el-select v-model="targetDeviceValue" placeholder="请选择" @change='selectRelationOption($event,"targetDevice","targetPointOptions")'>
                  <el-option v-for="(item,index) in targetDeviceOptions" :key="index" :label="item.label" :value="item.value" />
                </el-select> 
              </div>
            </template>
            <div class="section-title">源监测点选择</div>
            <div class="section-cont"> 
              <el-select v-model="targetPointValue" placeholder="请选择" @change='selectRelationOption($event,"targetPoint","targetPointOptions")'>
                <el-option v-for="(item,index) in targetPointOptions" :key="index" :label="item.label" :value="item.value" />
              </el-select> 
            </div>
            <el-button type="primary" @click="saveBasicInfo" class="save-basic-btn">保存配置</el-button>
            <el-button type="primary" @click="generate" class="save-basic-btn">点击生成随机图</el-button>
          </div>
        </el-tab-pane>
        <el-tab-pane label="其他配置" name="second">配置管理</el-tab-pane>
      </el-tabs>
    </div>
    <!-- DAG-Diagram主体 -->
    <!-- DataAll: 数据源
        updateDAG:每次动作行为会抛出最新的数据, 和对应事件名称.
        editNodeDetails: 可选内容,右键点击节点会带出节点数据,用此方法接受, 进行二次开发.比如更改节点携带的数据等.
        doSthPersonal: 属性来实现私有方法追加, 右键的面板会展示私有方法名.点击可以触发 doSthPersonal方法,  第一个参数为你定义的方法名, 第二个参数可以拿到节点id, 具体看demo即可. -->
    <DAGBoard ref="DAGBoard" :DataAll="yourJSONDataFillThere" @updateDAG="updateDAG" @editNodeDetails="editNodeDetails" @doSthPersonal="doSthPersonal"></DAGBoard>
    <!-- 用来模拟拖拽添加的元素 -->
    <node-bus v-if="dragBus" :value="busValue.value" :pos_x="busValue.pos_x" :pos_y="busValue.pos_y" />
  </div>
</template>
<script>
import { diagramExampleData, JSONFromService, nodesBasic } from './data.js'
import { login, selectTemplatePoint,templateDetail, scenceTreelist, scencePointlist, equipmentList, templateSave } from '@/api/model/index.js'
import { Message } from 'element-ui'
// import { scencePointlist,templateSave } from '@/api/model/index.js'

export default {
  name: 'Dag',
  components: {},
  props: {},
  data() {
    return {
      isShowRight: 1,// 是否显示右侧的编辑信息
      templateData: null, // 模型数据，如果是复制万之后，点击设计模型进入的，需要获取模型数据
      windowParams: {}, // 页面传递进来的参数
      scenceTree: [],
      data: diagramExampleData,
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      initNodesBasic: nodesBasic,
      // 以下为svg必要内容
      dragBus: false, // 是否在拖拽,
      yourJSONDataFillThere: { // 用来展示的节点与连线
        nodes: [],
        edges: []
      },
      // 以下为拖拽方式添加节点必须内容
      busValue: {
        value: "name",
        pos_x: 100,
        pos_y: 100
      },
      // 以下为表单展示数据
      formDetail: {
        currentEditNodeId: null,
        form: {
          sides: 10,
          nodes: 10
        }
      },
      // 监听的事件
      onkeydown: null,
      // 复制的内容
      copyContent: [],

      // 此处往下是右侧需要选择修改提交的数据
      activeName: 'first', // 右侧tab切换，选中的当前项
      deviceOptions: [
        { value: '1', label: '设备-1' },
        { value: '2', label: '设备-2' },
        { value: '3', label: '设备-3' },
        { value: '4', label: '设备-4' }
      ], // 设备下拉选项 { value: '1', label: '设备-1' }
      deviceValue: '',
      deviceTypeOptions: [], //设备类型下拉选项 { value: 'type1',label: '设备类型1' }
      deviceTypeValue: '',
      deviceParentOptions: [{
        value: '101',
        label: '源设备id-101'
      }, {
        value: '102',
        label: '源设备id-102'
      }, {
        value: '103',
        label: '源设备id-103'
      }],
      deviceParentValue: '',
      observationPointValue: '',
      observationPointOptions: [{
        value: '11',
        label: '监测点-11'
      }, {
        value: '22',
        label: '监测点-22'
      }, {
        value: '33',
        label: '监测点-33'
      }, {
        value: '44',
        label: '监测点-44'
      }],
      pointArrr: [],
      pointOptions: [
        // { name: '一一', label: 'one' },
        // { name: '二二', label: 'tow' },
        // { name: '三三', label: 'three' },
        // { name: '四四', label: 'four' },
        // { name: '五五', label: 'five' }
      ],
      srcTemplateValue: '', // 源模型 id 值
      srcTemplateOptions:[], // 源模型 下拉框
      srcDeviceValue:'', // 源设备值
      srcDeviceOptions:[], // 源设备下拉框 // { value: '101', label: '源设备id-101' }        
      srcPointValue:[], // 源监测点值
      srcPointOptions:[], // 源监测点下拉框 // { value: '101', label: '源监测点-101' } 
      targetTemplateValue:'',// 目标模型 id 值
      targetTemplateOptions:[], // 目标模型 下拉框
      targetDeviceValue:'', // 目标设备值
      targetDeviceOptions:[], // 目标设备下拉框
      targetPointValue:[], // 目标监测点值
      targetPointOptions:[], // 目标监测点下拉框
      checkNodeInfo: {}, // 选中的元素
      checkLineInfo: null, // 选中的线条
      srcNode: {}, // 源头元素
      targetNode: {}, // 目标元素
      //  {
      //   curEquipmentId: 0, // 当前选中的设备id
      // }

    }
  },
  created() {

    // this.loadJSON()
    this.onkeydown = document.addEventListener('keydown', e => {
      if (e.ctrlKey && e.key === 'c') {
        // 按下ctrl + c
        this.ctrlC()
      } else if (e.ctrlKey && e.key === 'v') {
        // 按下ctrl + v
        this.ctrlV()
      }
    });

    // 初始化页面参数，如果type是2，则代表修改，需要先获取模板的信息
    // let getParams = window.windowParams || {};
    /*let { modelType, templateId } = getParams;
    this.windowParams = getParams
    if (modelType == 2) {
      this.getTemplateData(templateId)
    }*/

console.log(templateDetail)

    // 获取地址栏里边传入的参数
    this.locationSearchToJson();
    // 初始化，获取所有的设备
    this.getEquipmentList();
    // 初始化获取，所有的模型列表数据
    this.getTemplateList();
    // 初始化，根据场景获取监测点的数据
    // this.getScencePointlist();
  },
  mounted() {},
  beforeDestroy() {
    this.onkeydown = null // 销毁事件
  },
  methods: {
    // 切换显示右侧的编辑信息区域
    toggleRightFrom(showNum = 0){
      this.isShowRight = showNum;
    },
    // 获取地址栏里的查询参数 
    // './edit/index.html?templateId=${data.templateId}&treeId=${treeId}&type=1'
    locationSearchToJson() {
      let getHref = location.href; //取得整个地址栏
      let num = getHref.indexOf("?")
      getHref = getHref.substr(num + 1); //取得所有参数 stringvar.substr(start [, length ]

      let arr = getHref.split("&"); //各个参数放到数组里
      let paramsJson = {}
      for (let i = 0; i < arr.length; i++) {
        num = arr[i].indexOf("=");
        if (num > 0) {
          let name = arr[i].substring(0, num);
          let value = arr[i].substr(num + 1);
          // this[name] = value;
          paramsJson[name] = value;
        }
      }
      console.log(` paramsJson ====== `);
      console.log(paramsJson);
      this.windowParams = paramsJson;
      // 如果有 模型id ，则利用模型id，获取模型的详细数据
      if (paramsJson&&paramsJson.templateId) {
        this.getTemplateData(paramsJson.templateId)
      }
    },
    // 点击去登录
    tapToLogin() {
      // 模拟登录之后，再请求获取数据接口
      login().then(res => {
        console.log(` toLogin res ==== `)
        console.log(res)
        // this.getScencePointlist();
      }).catch(err=>{
        console.log(err)
      });
    },
    tapGetTree() {
      // 左侧场景树
      this.initGetScenceTreelist()
    },
    // 初始化，获取左侧场景树数据
    initGetScenceTreelist() {
      scenceTreelist().then(res => {
        console.log(`scenceTreelist res ====== `);
        console.log(res);
        // 左侧场景树
        this.scenceTree = res.data;
      }).catch(err=>{
        console.log(err)
      });
    },
    // 点击左侧场景类型数目
    selectScenceTree(scenceId) {
      console.log(`scenceId: ${scenceId}`)
      this.getScencePointlist({ 'categoryId': scenceId })
    },
    // 根据模型 id，获取该模型的信息, 得到模型数据之后，转换成可渲染的data数据
    getTemplateData(id) {
      templateDetail({ 'templateId': id }).then(res => {
        console.log(`res ============== `)
        console.log(res)
        this.templateData = res.data;
        this.renderTemplateToData(res.data);
      }).catch(err=>{
        console.log(err)
      });
    },
    // 初始化，通过接口请求检测点数据
    getScencePointlist() {
      let { treeId } = this.windowParams;
      treeId = treeId || 2;
      let params = {
        'categoryId': treeId
      }
      console.log(` getScencePointlist ===== categoryId: ${treeId}, `)
      scencePointlist(params).then(res => {
        console.log(` scencePointlist res ok `)
        console.log(res)
        this.renderPointOptions(res.rows)
      }).catch(err => {
        console.error(` scencePointlist error `)
        console.log(err)
      })
    },
    // 获取所有的模型列表数据，用于下啦框选择
    getTemplateList(){
      let { treeId } = this.windowParams;
      treeId = treeId || 2;
      let params = {
        'sceneId': treeId
      }
      selectTemplatePoint(params).then(res=>{
        console.log(`data res ============ `);
        console.log( res );
        let getList = res.data;
        getList.forEach(item=>{
          item.value = item.templateId
          item.label = item.templateName
        })
        this.srcTemplateOptions = getList;
        this.targetTemplateOptions =getList;
      }).catch(err=>{
        console.log(err)
      });
    },
    // 获取所有的设备数据
    getEquipmentList(id=0) {
      let params = {'sceneId':id} 

      equipmentList(params).then(res => {
        console.log(`equipmentList data ============ `)
        console.log(res)
        this.rendeRequipmentOptions(res.data)
      }).catch(err=>{
        console.log(err)
      });
    },
    // 选择设备之后，需要在画布上进行更新
    selectDeviceId(val) {
      let getOption = this.deviceOptions.filter((item) => {
        if (item.value == val) {
          return item;
        }
      });
      if (getOption.length < 0) {
        return;
      }

      // this.checkNodeInfo.deviceId = getOption[0].value;
      // this.checkNodeInfo.name = getOption[0].label;
      let { value, label } = getOption[0];
      this.selectToUpdateJsonData({ 'deviceId': value, 'name': label });
      /*let nodesNata = this.yourJSONDataFillThere.nodes;
      for (let i = 0; i < nodesNata.length; i++) { 
        if (nodesNata[i].id == this.checkNodeInfo.id) {
          nodesNata[i] = this.checkNodeInfo;
          break;
        }
      } */
    },
    // 选择完监测点之后
    selectPointId(val) {
      // console.log(`val : ${ val }`) 
      /*let { id } = this.checkNodeInfo;
      this.checkNodeInfo.pointId = val;
      this.yourJSONDataFillThere.nodes.forEach(item => {
        if (id == item.id) {
          console.log(`id:${id} , item.id:${item.id}`)
        }
      })*/
      this.selectToUpdateJsonData({ 'pointId': val });
    },
    // 选择完设备只有（源设备||目标设备）
    selectRelationOption(val,keyName,pointKey){
      console.log(`val: ${val} ,keyName: ${keyName}`)
      console.log(`this.checkLineInfo ======= device `)
      console.log(this.checkLineInfo)
      if(!this.checkLineInfo){
        // console.log(`请在左侧选中线条后，在选择设备监测点属性~`);
          Message({
          message: '请在左侧选中线条后，再选择设备监测点属性~',
          type: 'none',
          duration: 5 * 1000
        })
        return false;
      }
      let {dst_input_idx,dst_node_id,id,src_node_id,src_output_idx} = this.checkLineInfo;
      console.log(`dst_input_idx: ${dst_input_idx} ,dst_node_id: ${dst_node_id}, id: ${id}, src_node_id: ${src_node_id}, src_output_idx: ${src_output_idx}`);
      let getLabel = this.returnSelectName(val,keyName).label;
      let {nodes} = this.yourJSONDataFillThere
      for (let i = 0; i <nodes.length; i++) {
        // console.log(`i ================ 1111111111: ${i}`)
        let nodeId = nodes[i].id;
        // 如果监测点json对象不存在，需要初始化json对象
        if (!nodes[i].pointIdJson) {
          nodes[i].pointIdJson = {}
        }
        // 源头元素
        if( src_node_id == nodeId){
          // 设置源头模板 id 
          if (keyName == 'srcTemplate' ) {
            nodes[i].templateId = val; // 设置源模板 id
            nodes[i].name = getLabel; // 设置源模板 名称
            this.getPointOptions(val,keyName,pointKey) // 选择完模板，获取监测点的值
          }
          // 设置源设备 id 
          if (keyName == 'srcDevice' ) {
            nodes[i].deviceId = val; // 设置源设备 id
            // nodes[i].name  = `源设备-${val}`;
            nodes[i].name = getLabel; // 设置源设备 名称
            this.getPointOptions(val,keyName,pointKey) // 选择完设备，获取监测点的值
          }
          // 源监测点
          if (keyName == 'srcPoint') {          
            nodes[i].pointIdJson[src_output_idx] = val; // 设置监测点 id
            // nodes[i].pointIdJson[src_output_idx] = getLabel; // 设置监测点 name
          }
        } 
        // 目标元素
        if( dst_node_id == nodeId){          
          // 设置目标设备 id 
          if (keyName == 'targetTemplate' ) {
            nodes[i].deviceId = val; 
            nodes[i].name  = getLabel; 
            this.getPointOptions(val,keyName,pointKey) // 选择完模板，获取监测点的值
          }
          // 设置目标设备 id 
          if (keyName == 'targetDevice' ) {
            nodes[i].deviceId = val;
            nodes[i].name  = getLabel; 
            this.getPointOptions(val,keyName,pointKey) // 选择完设备，获取监测点的值
          }
          // 设置目标监测点 id
          if (keyName == 'targetPoint') { 
            nodes[i].pointIdJson[dst_input_idx] = val; // 设置监测点 id
            // nodes[i].pointIdJson[dst_input_idx] = getLabel; // 设置监测点 name
          }
        }
        // console.log(`i ================ 222: ${i}`)
        // console.log(nodes[i])
      }
      // 修改完之后，更新画布数据
      this.yourJSONDataFillThere.nodes = nodes;
    },
    // 根据已选择的value， 返回下啦框的 name 值
    returnSelectName(val,keyName){
      let checkItem = null;
      let setKey = `${keyName}Options`
      this[setKey].forEach(item =>{
        if (item.value == val) {
          checkItem = item;
        }
      });
      console.log(` checkItem ================ `)
      console.log(checkItem)
      return checkItem;
    },
    // 选择完模型或设备之后，根据模型id 或 设备id，获取对应的监测点下拉框数据
    getPointOptions(val,keyName,pointKey){
      let setKey = `${keyName}Options`; 
      let newArr = [];
      this[setKey].forEach( item =>{
        if (item.value == val) {
          console.log(`val : ${val}`);
          console.log(item) 
          newArr = item.pointList;
        }
      });

      let pointArr = []; // 监测点数组
      newArr.forEach(item =>{
        pointArr.push({ 
          ...item,
          "label": item.pointName,
          "value": item.pointId
        })
      });
      this[pointKey] = pointArr;
      console.log(`val pointArr ================  `)
      console.log(pointArr)

    },
    // 选择完右侧的下啦框之后，更新画布上数据节点的值
    selectToUpdateJsonData(params) {
      if (!this.checkNodeInfo) {
        Message({
          message: '请在左侧选中设备后，在选择您要更改的设备',
          type: 'error',
          duration: 5 * 1000
        })
        return;
      }
      this.checkNodeInfo = Object.assign(this.checkNodeInfo, params);
      let nodesNata = this.yourJSONDataFillThere.nodes;
      for (let i = 0; i < nodesNata.length; i++) {
        if (nodesNata[i].id == this.checkNodeInfo.id) {
          nodesNata[i] = this.checkNodeInfo;
          break;
        }
      }
      this.yourJSONDataFillThere.nodes = nodesNata;
    },
    // 把根据模型id，获取到的模型数据，转换成可在画布上显示的数据，（线条、和元素）
    renderTemplateToData(data) {
      let edges = []; // 线条数组
      let nodes = []; // 设备数组
      console.log(edges, nodes)

      /*if (data.templateDetailList) {
        console.log(edges, nodes)
      }*/
      this.yourJSONDataFillThere.edges = data.edges?data.edges: [];
      this.yourJSONDataFillThere.nodes = data.nodes?data.nodes: [];
    },
    // 得到设备数据之后，转换成下拉框可用的数据
    rendeRequipmentOptions(data) {
      console.log(' 得到设备数据之后，转换成下拉框可用的数据 rendeRequipmentOptions =========== '); 
      if(!data || data.length<1 ){
        console.log('暂无数据，不需要渲染');
        return false;
      }
      let newArr = data || [];
      if ( data.length > 10) {
        newArr = data.slice(0, 10)
      }
      console.log(`1、 data : ${data.length} , newArr: ${newArr.length}`)
      newArr.forEach((item, index) => {
        console.log(item)
        newArr[index] = Object.assign(newArr[index], {
          pointId: item.equipmentId,
          value: item.equipmentId,
          name: item.equipmentName,
          label: item.equipmentName
        })
      });
      // this.pointOptions = newArr; 
      this.deviceOptions = newArr;      
      this.srcDeviceOptions = newArr;
      this.targetDeviceOptions = newArr;
    },
    // 得到监测点数据之后，转换成下拉框可用的数据
    renderPointOptions(data) {
      let newArr = [];
      if (data.length > 10) {
        newArr = data.slice(0, 10)
      }
      console.log(`1、 data : ${data.length} , newArr: ${newArr.length}`)
      newArr.forEach((item, index) => {
        console.log(item)

        newArr[index] = Object.assign(newArr[index], {
          pointId: item.pointId,
          value: item.pointId,
          name: item.pointName,
          label: item.pointName
        })
      });
      // this.pointOptions = newArr;
      this.observationPointOptions = newArr;
    },
    generate() { // 生成随机图
      const detaultNumber = 100; // 默认的数量
      const nodesNumber = this.formDetail.form.nodes || 10; // 节点数量
      const sidesNumber = this.formDetail.form.sides || 10; // 边的数量
      const GRAPH_WIDTH = (nodesNumber > detaultNumber ? nodesNumber : detaultNumber) * 10; // 图的宽度
      const GRAPH_HEIGHT = (nodesNumber > detaultNumber ? nodesNumber : detaultNumber) * 10; // 图的高度
      const newGraphNodes = [];
      const newGraphSides = [];
      this.yourJSONDataFillThere = {
        nodes: [],
        edges: []
      }
      // step1 生成节点
      const generatePosition = (eq) => { // 生成坐标
        const PosX = (Math.random() * GRAPH_WIDTH).toFixed();
        const PosY = (Math.random() * GRAPH_HEIGHT).toFixed();
        console.log(`eq: ${eq}, PosX : ${PosX},PosY : ${PosY}`)
        return {
          pos_x: Number(PosX),
          pos_y: Number(PosY)
        }
      }
      for (let nodeIndex = 0; nodeIndex < nodesNumber; nodeIndex++) {
        const eachGraphNode = {
          "id": nodeIndex + 1,
          "in_ports": [0],
          "name": `NODE_${nodeIndex}`,
          "out_ports": [0],
          "detail": [],
          ...generatePosition(nodeIndex)
        }
        newGraphNodes.push(eachGraphNode);
      }
      this.yourJSONDataFillThere.nodes = newGraphNodes
      // step2 生成边 因为id是有序的,所以这里id范围根据节点生成
      const generateNodeEntry = (nodesMax) => { return (Math.random() * nodesMax).toFixed(); }
      for (let sideIndex = 0; sideIndex < sidesNumber; sideIndex++) {
        const eachSide = {
          "dst_input_idx": 0,
          "dst_node_id": Number(generateNodeEntry(nodesNumber)) || 1,
          "id": sideIndex + 1,
          "src_node_id": Number(generateNodeEntry(nodesNumber)) || 1,
          "src_output_idx": 0,
          "edgesText": ""
        }
        if (eachSide.dst_node_id !== eachSide.src_node_id) {
          newGraphSides.push(eachSide);
        }
      }
      this.yourJSONDataFillThere.edges = newGraphSides
      console.log(JSON.stringify(this.yourJSONDataFillThere, null, 4))
    },
    autoLayout() { // 自动布局
      const allSidesArr = this.yourJSONDataFillThere.edges.map(i => [i.src_node_id, i.dst_node_id]); // [[1,2], [2,3]]
      const connectTest = (a, b) => { // 测试关联 [1, 2], [2, 3]
        if (a[0] === b[1] || b[0] === a[1] || a[0] === b[0] || a[1] === b[1]) {
          return true
        } else {
          return false
        }
      }
      let arrSeparate = [
        [allSidesArr.shift()]
      ];
      const testArr = (allSidesArr) => { // 递归检测连线的关系,把有关联的连线放在一起
        for (let i = 0; i < arrSeparate[arrSeparate.length - 1].length; i++) {
          for (let j = 0; j < allSidesArr.length; j++) {
            if (connectTest(arrSeparate[arrSeparate.length - 1][i], allSidesArr[j])) { // 建立关联,放入分组
              arrSeparate[arrSeparate.length - 1] = arrSeparate[arrSeparate.length - 1].concat(allSidesArr.splice(j, 1));
              testArr(allSidesArr) // 递归
              return false;
            }
          }
        }
        // 循环完毕仍然有数据的话, 放入第二个数组
        if (allSidesArr.length) {
          arrSeparate.push(allSidesArr.shift());
          testArr(allSidesArr);
        }
      }
      testArr(allSidesArr)
      console.log("整理好的数据", arrSeparate)
      // while (arrSeparate.length) { // 递归画所有的关系图
      //   this.drawSingleGraph(arrSeparate.shift());
      // }
      this.drawSingleGraph(arrSeparate.shift()); // 先画一个
    },
    unique(arr) { // 数组去重
      return Array.from(new Set(arr))
    },
    drawSingleGraph(graphArr) {
      const left = graphArr.map(v => v[0]); // 出口集合
      const right = graphArr.map(v => v[1]); // 入口集合
      const result = [
        []
      ];
      // 寻找最上层节点 只有出口没有入口的都是顶点
      result[0] = left.filter(v => right.indexOf(v) === -1);
      // 没有上类节点的图证明成环,放弃渲染 有顶点才会渲染
      let recursionArr = graphArr // 全部数组  [[1,2], [2,3], [3,4]]...
      if (result[0].length) {
        result[0] = this.unique(result[0]);
        while (recursionArr.length) { // 剩余连线存在就一直递归
          // recursionArr = graphArr.filter(eachArr => result[0].indexOf(eachArr[0]) === -1); // 更新剩余的边
          let nextLevelNodesArr = []
          /* result[result.length - 1].map((sourceFromId) => { // 当前循环到的层级
            recursionArr.map(eachArr => {
              if (result[result.length - 1].indexOf(eachArr[0]) > -1) { // 检测到入口对应的边,把出口id放入数组下一级
                 nextLevelNodesArr.push(eachArr[1])
              }
            })
          }) */
          result[result.length - 1].map(() => { // 当前循环到的层级
            recursionArr.map(eachArr => {
              if (result[result.length - 1].indexOf(eachArr[0]) > -1) { // 检测到入口对应的边,把出口id放入数组下一级
                nextLevelNodesArr.push(eachArr[1])
              }
            })
          })
          recursionArr = recursionArr.filter(eachArr => result[result.length - 1].indexOf(eachArr[0]) === -1) // 更新剩余的边
          if (nextLevelNodesArr.length) {
            nextLevelNodesArr = this.unique(nextLevelNodesArr);
            result[result.length] = nextLevelNodesArr
          }
        }
      }
      // debugger
      this.draw(result);
    },
    draw(nodesIdArr) {
      // const newNodesJSON = [];
      const CANVAS_WIDTH = 1440;
      const EACH_LEVEL_HIGH = 300;
      this.yourJSONDataFillThere.nodes.map(v => {
        v.pos_x = -1000;
        v.pox_y = 0;
      })
      nodesIdArr.forEach((row, rowIndex) => {
        row.map((curId, curIndex) => {
          const aim = this.yourJSONDataFillThere.nodes.find(v => v.id === curId);
          aim.pos_x = (CANVAS_WIDTH / row.length + 1) * (curIndex + 1);
          aim.pos_y = EACH_LEVEL_HIGH * (rowIndex + 1);
        })
      })
    },
    ctrlC() {
      let currentChoice = this.$refs.DAGBoard.choice
      if (currentChoice.index > -1) { // 有选中元素
        let activeNodes = this.yourJSONDataFillThere.nodes.filter(item => currentChoice.paneNode.indexOf(item.id) > -1)
        this.copyContent = JSON.parse(JSON.stringify(activeNodes))
        this.copyContent.forEach(item => {
          item.id = item.id + this.yourJSONDataFillThere.nodes.length + 100 // 自定义id规范 这里随便写个长度+100作为id
          item.pos_x += (100 / (sessionStorage['svgScale'] || 1))
          item.pos_y += (100 / (sessionStorage['svgScale'] || 1))
        })
      }
    },
    ctrlV() {
      if (!this.copyContent.length) return false
      this.yourJSONDataFillThere.nodes.push(...this.copyContent)
      this.$refs.DAGBoard.choice = {
        paneNode: [], // 选取的节点下标组
        index: -1,
        point: -1
      } // 复制完成 取消选中状态
      this.copyContent = []
    },
    updateDAG(data, action, id) {
      // console.log(`updateDAG ======================= action: ${action} `)
      // console.log(...arguments)
      // console.log(JSON.stringify(data))
      switch (action) {
        case 'selectNode': //
          this.showNodeDetails(data.nodes.find(item => item.id === id))
          break;
        case 'focusCurNode': // 选中元素，展示右侧下啦框，选择之后绑定关联关系
          // console.log(`data : ${data}, action : ${action}, id: ${id}`)
          // console.log(`data : ${data}, action : ${action}, id: ${id}`)
          // this.showNodeDetails(data.nodes.find(item => item.id === id));
          this.checkNodeRightAttribute(data);
          break;          
        case 'checkCurPath': // 选中线条，展示右侧下啦框，选择之后绑定关联关系
          this.checkLineRightAttribute(data);
          break;
           
        default: () => {}
      }
    },
    editNodeDetails() {
      console.log('editNodeDetails ======================= ')
      console.log(...arguments)
    },
    doSthPersonal() {
      console.log('doSthPersonal ======================= ')
      console.log(...arguments)
    },
    loadJSON() {
      // 这里可以跟服务端交互获取数据
      setTimeout(() => {
        this.yourJSONDataFillThere = JSONFromService
      }, 500);
    },
    // 获取经过计算的 x y 的坐标
    getNodesXY(e) {
      /*const slideBarBox = document.getElementsByClassName('sidebar-container')[0];
      const rightTopHeadBox = document.getElementsByClassName('right-top-head')[0];
      const slideWidth = slideBarBox.offsetWidth
      const rightTopHeight = rightTopHeadBox.offsetHeight      
      
      return {
        x: e.pageX - slideWidth,
        y: e.pageY - rightTopHeight
      }*/

      // let exampleView = document.getElementById('diagram-example-vue');
      // let rectInfo = exampleView.getBoundingClientRect();
      // console.log(rectInfo)
      /* rectInfo:{
            bottom :  335.3333435058594,
            height: 236.6666717529297,
            left: 0,
            right: 1193.3333740234375,
            top: 98.66667175292969,
            width: 1193.3333740234375,
            x: 0,
            y: 98.66667175292969
        }*/
      return {
        x: e.pageX,
        y: e.pageY
      }

    },
    /**
     * 通过拖拽方式加入新节点必须的函数
     */
    startNodesBus(e) {
      /**
       *  别的组件调用时, 先放入缓存
       * dragDes: {
       *    drag: true,
       *    name: 组件名称
       *    type: 组件类型
       *    model_id: 跟后台交互使用
       * }
       **/
      let dragDes = null;
      if (sessionStorage["dragDes"]) {
        dragDes = JSON.parse(sessionStorage["dragDes"]);
      }
      if (dragDes && dragDes.drag) {
        // const x = e.pageX;
        // const y = e.pageY;

        const x = this.getNodesXY(e).x
        const y = this.getNodesXY(e).y
        this.busValue = Object.assign({}, this.busValue, {
          pos_x: x,
          pos_y: y,
          value: dragDes.name
        });
        this.dragBus = true;
      }
    },
    moveNodesBus(e) {
      // 移动模拟节点
      if (this.dragBus) {
        const x = e.pageX;
        const y = e.pageY;
        // console.log(`moveNodesBus x: ${x}, y:${y}`)
        this.busValue = Object.assign({}, this.busValue, {
          pos_x: x,
          pos_y: y
        });
      }
    },
    endNodesBus(e) {
      // 节点放入svg
      let dragDes = null;
      if (sessionStorage["dragDes"]) {
        dragDes = JSON.parse(sessionStorage["dragDes"]);
      }
      if (dragDes && dragDes.drag && e.toElement.id === "svgContent") {
        const pos_x =
          (e.offsetX - 90 - (sessionStorage["svg_left"] || 0)) /
          (sessionStorage["svgScale"] || 1); // 参数修正
        const pos_y =
          (e.offsetY - 15 - (sessionStorage["svg_top"] || 0)) /
          (sessionStorage["svgScale"] || 1); // 参数修正
        delete dragDes.drag
        const params = {
          model_id: sessionStorage["newGraph"],
          desp: {
            pos_x,
            pos_y,
            name: this.busValue.value,
            ...dragDes
          }
        };
        // console.log(`params.desp ================== `)
        // console.log(params.desp)
        this.yourJSONDataFillThere.nodes.push({
          ...params.desp,
          id: this.yourJSONDataFillThere.nodes.length + 100, // 这里注意, 新增的id一定不能重复, 建议id交由后端处理
          in_ports: [0],
          out_ports: [0] 
        })
        // out_ports: params.desp.out_ports?params.desp.out_ports:[0] // 如有有传入值，则重新设置出点
      }
      window.sessionStorage["dragDes"] = null;
      this.dragBus = false;
    },
    dragIt(val) {

      val.form.createTime = new Date().toDateString()
      sessionStorage["dragDes"] = JSON.stringify({
        drag: true,
        ...val
      });
    },
    /**
     * 右侧form展示逻辑
     */
    showNodeDetails(val) { // 展示选中的节点
      const { id, form } = val
      if (!form) return
      this.formDetail = {
        currentEditNodeId: id,
        form: Object.assign(this.formDetail.form, form, {})
      }
    },
    onSubmit() { // 更新所选节点的信息
      const { currentEditNodeId, form } = this.formDetail
      this.yourJSONDataFillThere.nodes.map(item => {
        if (item.id === currentEditNodeId) {
          item.form = form
        }
      })
      let _data = this.yourJSONDataFillThere.nodes.find(item => item.id === currentEditNodeId).form
      let str = JSON.stringify(_data, null, 4)
      alert('节点信息更新完成' + str)
    },
    // 右侧 tab 切换执行的函数
    rightTabsClick(tab, event) {
      console.log(tab, event)
    },
    // select 选择元素的源设备值
    selectDeviceParentValue(nodeId) {
      console.log(` selectDeviceParentValue e `)
      console.log(nodeId)
      if (!nodeId) {
        return
      }
      let checkNodeId = this.checkNodeInfo ? this.checkNodeInfo.id : 0;
      console.log(`checkNodeId : ${checkNodeId} , nodeId: ${nodeId}`)
      console.log(typeof nodeId)
      //保存线的信息，包括起始点id，目标点id 。
      let lineInfo = {
        "src_node_id": Number(nodeId), // 起始点id
        "src_output_idx": 0,
        "dst_node_id": checkNodeId, // 目标点id (箭头执行的node元素id)
        "dst_input_idx": 0
      }
      lineInfo.id = this.yourJSONDataFillThere.edges.length * 1000 + 1;
      this.yourJSONDataFillThere.edges.push(lineInfo)
    },


    // 监测点全选操作
    selectAllPoint() {
      if (this.pointArrr.length < this.pointOptions.length) {
        this.pointArrr = []
        this.pointOptions.map((item) => {
          this.pointArrr.push(item.name)
        })
        this.pointArrr.unshift('全选')
      } else {
        this.pointArrr = []
      }
    },

    removeTag(val) {
      if (val === '全选') {
        this.pointArrr = []
      }
    },
    // 点击右侧基本设置的保存
    saveBasicInfo() {

      let { edges, nodes } = this.yourJSONDataFillThere;
      console.log(`edges : ${edges.length}, nodes: ${nodes.length}`)
      // let nodesIdArr = nodes.map(item=> item.id);
      // let relationNumer = 0; // 所有的设备是否都关联了，如果都关联了，个数应该等于设备数
      let relationJson = {}; // 判断所有的设备是不是都关联了
      // let isAllRelation = true; // 所有的node都连接起来了。 
      let msgStr = ''; //定义提示语，有提示语不能提交
      for (let i = 0; i < nodes.length; i++) {
        let getId = nodes[i].id;
        let isAbout = false; //是否有线连接。
        for (let j = 0; j < edges.length; j++) {
          let { dst_node_id, src_node_id } = edges[j];
          // console.log(`dst_node_id:${dst_node_id},src_node_id:${src_node_id},getId:${getId}`)
          let keyStr = `${getId}`
          if (!relationJson[keyStr]) {
            relationJson[keyStr] = {};
          }
          if (dst_node_id == keyStr) {
            relationJson[keyStr]['dst_node_id'] = relationJson[keyStr]['dst_node_id'] ? relationJson[keyStr]['dst_node_id'] + 1 : 1;
          }
          if (src_node_id == keyStr) {
            relationJson[keyStr]['src_node_id'] = relationJson[keyStr]['src_node_id'] ? relationJson[keyStr]['src_node_id'] + 1 : 1;
          }
          // 必须有一个进入点或者出点， 并且入点和出点的id 不能相同
          if (((dst_node_id == keyStr) || (src_node_id == keyStr)) && (dst_node_id != src_node_id)) {
            isAbout = true;
            // 如果线的源头id，等于node元素的id，并且没有出点的时候，说明是源头
            if(src_node_id == keyStr){
              relationJson[keyStr]['sourceTop']=1;
            }
            // 只有出点，说明是头部元素
            if(src_node_id == keyStr){
              relationJson[keyStr]['targetEnd']=1; 
            }
          }
        }
        if (!isAbout) {
          msgStr = '有未关联的设备，请您关联后再提交~';
          break;
        }
      }

      console.log(`relationJson data ===================== `)
      console.log(relationJson)
      // 判断是不是有多个起始点（只有出点，没有入店）    
      let topNodeNumber = 0; // 源点个数，
      let topNodeId = 0; // 源点 id
      let endArr = [] ;// 节点数组，可能存在多个结束点，所以需要用数组记录一下
      for (let key in relationJson) {
        if (relationJson[key]['src_node_id'] && !relationJson[key]['dst_node_id']) {
          topNodeNumber += 1;
          topNodeId = key;
        }
        if (relationJson[key]['dst_node_id'] && !relationJson[key]['src_node_id']) {
          endArr.push(Number(key))
        }
          
      }
      if (topNodeNumber > 1) {
        msgStr = '您的模型可能存在多个源点，请您修改后再保存~';
      }


      // 有提示语，不能提交
      if (msgStr) {
        Message({
          message: msgStr,
          type: 'error',
          duration: 2 * 1000
        })
        return;
      }

      let { treeId,templateId } = this.windowParams || ''
      let params = {
        template: {
          // templateName: '测试模型'+ new Date(), // string 模板名称
          templateId: templateId, // 模型 id
          status: '', // string 状态
          sceneId: treeId // integer 场景id
        },
        templateDetailList: [], // 模型的每一组（源头=》目标）对应的数据
        edges: edges, // 用于前端显示的线， 数组形式
        nodes: nodes // 用于前端展示的元素 ，数组形式  代表模型里的设备
      }



      // 0: 设备, 1: 模型
      // sourceType: string //源类型,模型,设备
      // targetType: string//目标类型,模型,设备
      // 
      //  sourceTop:  // 是否为头部  =》 1：是；0：否
      //  targetEnd: // 是否为尾部 =》 1：是；0：否
      let listData = [];
      for (let i = 0; i < edges.length; i++) {
        let itemJSON = {
          'sourceEquipmentId': edges[i].src_node_id, // 源设备id 
          'targetEquipmentId': edges[i].dst_node_id // integer 目标设备id
        }
        console.log(` pointJson ============== `)
        // console.log(pointJson)
        // 22-9-27
        for (let j = 0; j < nodes.length; j++) {

          let pointJson = nodes[j]['pointIdJson'];
          // 获取当前节点的元素类型，0：设备； 1：模型
          let getType = nodes[j].nodeType
          // 设置源头设备的 x y 的坐标，以及源监测点属性
          if (nodes[j].id == itemJSON.sourceEquipmentId) {
            // 添加源头节点特殊标识， type:1,代表源头
            if (topNodeId == nodes[j].id) {
              itemJSON['sourceTop'] = 1;
              // itemJSON['sourceType'] 
            }
            itemJSON['sourceEquipmentLocaionX'] = nodes[j].pos_x // integer 源设备x轴坐标
            itemJSON['sourceEquipmentLocaionY'] = nodes[j].pos_y // integer 源设备y轴坐标
            itemJSON['sourcePointId'] = nodes[j].pointId;
            itemJSON['sourceType'] = getType;
            // 22-9-27
            // 根据索引获取 pointJson 对应的监测点id
            itemJSON['sourcePointId'] = pointJson[edges[i].src_output_idx];            

            // 需要吧id改成正确的 模板id 或 设备id
            itemJSON['sourceEquipmentId'] = (getType==1?nodes[j].templateId:nodes[j].deviceId);
          } else {
            itemJSON['targetEquipmentLocaionX'] = nodes[j].pos_x // integer 目标设备x轴坐标
            itemJSON['targetEquipmentLocaionY'] = nodes[j].pos_y // integer 目标设备y轴坐标
            itemJSON['targetPointId'] = nodes[j].pointId            
            itemJSON['targetType'] = getType;
            // 22-9-27
            // 根据索引获取 pointJson 对应的监测点id
            itemJSON['targetPointId'] = pointJson[edges[i].dst_input_idx];

            // 需要吧id改成正确的 模板id 或 设备id
            itemJSON['targetEquipmentId'] = (getType==1?nodes[j].deviceId:nodes[j].templateId);
            console.log(`nodes[j].id:${nodes[j].id},itemJSON.targetEquipmentId:${itemJSON.targetEquipmentId}`)
            // console.log(endArr)
            // 如果当前元素在，结束数组中包含着，则设置结束标识
            if(endArr.indexOf(Number(itemJSON.targetEquipmentId))>=0){
              console.log(`1 ==================== >>>>> `)
              itemJSON['targetEnd'] = 1;
            }
          }
        }
        listData.push(itemJSON);
      }
      params.templateDetailList = listData;
      console.log(` 点击右侧的保存配置按钮 == listData `)
      console.log(listData)
      // console.log(endArr)


      templateSave(params).then(res => {
        console.log(` templateSave res ok `)
        console.log(res)
      }).catch(err => {
        console.error(` templateSave error `)
        console.log(err)
      })
    },
    // 选中对应的元素，需要把该元素的属性值在右侧展示
    checkNodeRightAttribute(data) {
      if (!data) {
        return
      }
      let nodeInfo = null;
      data.nodes.forEach(item => {
        if (item.checkCurNode) {
          nodeInfo = item;
        }
      })
      console.log(` nodeInfo ================== `)
      console.log(nodeInfo)

      this.yourJSONDataFillThere = data;
      this.updateRightNodeAttribute(nodeInfo)
    },
    // 选中线条之后，选择源头和目标的设备id 和监测点属性
    checkLineRightAttribute(data){
      // console.log(`checkLineRightAttribute data`)
      // console.log(data)
      let lineInfo = null;
      data.edges.forEach(item => {
        if (item.isCheck) {
          lineInfo = item;
        }
      });
      this.checkLineInfo = lineInfo;
      this.yourJSONDataFillThere = data;
      // this.$forceUpdate() ;
      console.log(` data lineInfo ===================== `)
      console.log(lineInfo)
      let srcNode = null, // 源头元素
      targetNode = null;  // 目标指向元素
      if(!lineInfo){
        return ;
      }
      let {nodes} = this.yourJSONDataFillThere;
      nodes.forEach((item,i)=>{
        item.index = i;
        // 源头元素
        if (lineInfo.src_node_id == item.id) {
          srcNode = item; 
        } 
        // 目标元素
        if (lineInfo.dst_node_id == item.id) {
          targetNode = item; 
        } 
      });
      console.log(` srcNode ===================== `)
      console.log(srcNode)
      console.log(` targetNode ===================== `)
      console.log(targetNode) 
      this.srcNode = srcNode;
      this.targetNode = targetNode;


    },
    // 更新选中点的右侧数据的展示
    updateRightNodeAttribute(nodeInfo) {
      this.checkNodeInfo = nodeInfo;
      // deviceId: 设备id，如果有设备id，则把右侧的设备value值对应改变一下
      this.deviceValue = nodeInfo.deviceId ? nodeInfo.deviceId : '';
      // pointId：监测点 id，如果有监测点id，则把右侧的监测点value值对应改变一下
      this.observationPointValue = nodeInfo.pointId ? nodeInfo.pointId : '';
    }
  }
}
</script>
<style lang="scss">
.diagram-example-vue {
  width: 100%;
  height: 100vh;
  overflow: hidden;
  position: relative;

  .basic-node {
    margin: 5px auto;
    background: #fff;
    color: black;
    border-radius: 15px;
    height: 30px;
    width: 140px;
    border: 1px solid #289de9;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    user-select: none;
    overflow: hidden;

    .icon {
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 24px;
      color: #fff;
      width: 28px;
      height: 28px;
      background: #289de9;
      border-radius: 50%;
      margin: 1px 10px 1px 1px;
    }

    .node-text {
      flex: 1;
    }
  }

  .page-left {
    position: absolute;
    left: 0;
    top: 0;
    width: 160px;
  }

  .tree-ul {
    color: #000;

    .item-text {
      padding-left: 20px;
    }
  }

  .right-form {
    width: 245px;
    position: absolute;
    position: absolute;
    right: 0;
    top: 0;
    height: 100%;
    border-left: 1px solid #ccc;
    background: #fff;

    .title {
      font-size: 20px;
      line-height: 80px;
    }

    .config-tabs {
      padding: 0 20px;

      .el-tabs__nav-wrap {
        display: flex;
        justify-content: center;
      }
    }

    .section-title {
      padding: 10px 0;
      font-size: 14px;
      line-height: 1.6;
      color: #666;
    }

    .section-cont {
      padding: 0 10px;

      .select-title {
        margin-top: 8px;
        font-size: 12px;
        line-height: 1.6;
        color: #999;
      }
    }

    .save-basic-btn {
      display: block;
      margin: 20px auto;
    }


    .tab-cont{ height: 500px; overflow-y: auto; }

  }
}

/** 给svg添加格子背景，这个是拖拽的svg画布 */
#svgContent {
  background-size: 50px 50px;
  background-image: linear-gradient(0deg,
    transparent 24%,
    rgba(255, 255, 255, 0.05) 25%,
    rgba(255, 255, 255, 0.05) 26%,
    transparent 27%,
    transparent 74%,
    rgba(255, 255, 255, 0.05) 75%,
    rgba(255, 255, 255, 0.05) 76%,
    transparent 77%,
    transparent),
    linear-gradient(90deg,
    transparent 24%,
    rgba(255, 255, 255, 0.05) 25%,
    rgba(255, 255, 255, 0.05) 26%,
    transparent 27%,
    transparent 74%,
    rgba(255, 255, 255, 0.05) 75%,
    rgba(255, 255, 255, 0.05) 76%,
    transparent 77%,
    transparent);
  background-color: rgb(60, 60, 60) !important;
  margin-left: 160px;
  margin-right: 200px;
}

</style>
